import thread
import socket
import struct
import asyncore
import gobject
import time
from util import device

MAX_BUF_SIZE = 4096

import logging
logger = logging.getLogger('network')

def get_broadcast_addr(iface):
    if device.current() == device.WINDOWS:
        # TODO: Implement this properly for windows.
        return '192.168.2.255'
    else:
        import fcntl
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
            s.fileno(), 
            0x8919, # SIOCGIFBRDADDR
            struct.pack('256s', iface[:15])
        )[20:24])

def get_ip_addr(iface):
    if device.current() == device.WINDOWS:
        # TODO: Implement this properly for windows.
        return '192.168.2.2'
    else:
        import fcntl
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
                s.fileno(), 
                0x8915, # SIOCGIFADDR
                struct.pack('256s', iface[:15])
        )[20:24])

def start():
    thread.start_new_thread(asyncore.loop,(0.001, True))


class NetTune:
    
    def __init__(self):
        self._last_sent = time.time()
        self._interval = 0.01
        
    def set_interval(self, interval):
        self._interval = interval
            
class Broadcast(asyncore.dispatcher, NetTune):
        
    def __init__(self, iface, recv_port, queue, rec_func):
        asyncore.dispatcher.__init__(self)
        NetTune.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.setblocking(1)
        self.__my_ip = get_ip_addr(iface)
        self.__broadcast_ip = get_broadcast_addr(iface)
        self.__recv_port = recv_port
        self.__queue = queue
        self.__rec_func = rec_func
        self.bind(('', self.__recv_port))
        logger.debug('Broadcast: listening on %s:%s. Broadcasting to %s',self.__my_ip, self.__recv_port, self.__broadcast_ip)
    
    def handle_connect(self):
        pass

    def handle_read(self):
        data, addr = self.recvfrom(MAX_BUF_SIZE)
        if addr == (self.__my_ip, self.__recv_port):
            return
        else:
            self.__rec_func(data, addr)
           
    def handle_write (self):
        data, addr = self.__queue.next()
        if data is None:
            logger.info('Broadcast: data was none')
            return
        ip, port = addr
        if not port:
            port = self.__recv_port
        addr = (self.__broadcast_ip, port)
        sent = 0
        while sent < len(data):
            sent = self.sendto(data, addr)
        logger.debug('Broadcast: Sent %i bytes to %s', sent, addr)
        self._last_sent = time.time()
      
    def writable (self):
        return len(self.__queue) > 0 and time.time() - self._last_sent > self._interval
        

class Unicast(asyncore.dispatcher, gobject.GObject, NetTune):
    
    def __init__(self, iface, recv_port, queue, rec_func):
        asyncore.dispatcher.__init__(self)
        NetTune.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.setblocking(1)
        self.__my_ip = get_ip_addr(iface)
        self.__recv_port = recv_port
        self.__queue = queue
        self.__rec_func = rec_func
        self.bind(('', self.__recv_port))
        logger.debug('Unicast: listening on %s:%s',self.__my_ip, self.__recv_port)

    def handle_connect(self):
        pass
        
    def handle_read(self):
        data, addr = self.recvfrom(MAX_BUF_SIZE)
        logger.debug('Unicast: read %s bytes from %s', len(data), addr)
        self.__rec_func(data, addr)

    def handle_write (self):
        data, addr = self.__queue.next()
        
        if data is None:
            logger.warn('Unicast: data was none')
            return

        ip, port = addr
        if not port:
            port = self.__recv_port
        addr = (ip, port)
        logger.debug('Unicast: writing %s bytes to %s', len(data),addr)
        sent = 0
        while sent < len(data):
            sent = self.sendto(data, addr)
        logger.debug('Unicast: wrote %s bytes to %s',len(data), addr)
        self._last_sent = time.time()

    def writable (self):
        return len(self.__queue) > 0 and time.time() - self._last_sent > self._interval